﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Color = System.Drawing.Color;
using System.Drawing.Design;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Reflection;
using ImageEffect.Effects;

namespace ImageEffect
{
    public partial class ImageEffect : Form
    {
        Bitmap Background;

        public ImageEffect()
        {
            InitializeComponent();

            Background = new Bitmap(int.Parse(imageWidth.Text), int.Parse(imageHeight.Text));

            EffectPropertyGrid.SelectedObject = this.Effects;

            // Initialize our dropdown
            effectsCombo.Items.AddRange(Effect.EffectsList);

            imagePreview.SizeMode = PictureBoxSizeMode.StretchImage;

            AddTestEffects();

            UpdateImage();
        }

        private void AddTestEffects()
        {
            // Startup with an amazing array of effects!
            HorizontalGradient hg = new HorizontalGradient();
            hg.StartColor = Color.White;
            hg.EndColor = Color.Blue;

            VerticalGradient vg = new VerticalGradient();
            vg.StartColor = Color.FromArgb(121, 116, 255, 166);
            vg.EndColor = Color.FromArgb(107, 210, 0, 0);

            BoxEffect be = new BoxEffect();
            be.MaxOpacity = 15;
            be.SideLength = 30;

            Scanlines sl = new Scanlines();
            sl.Color = Color.FromArgb(125, 255, 255, 255);
            sl.Interval = 30;
            sl.Angle = 0;

            this.Effects.Add(hg);
            this.Effects.Add(vg);
            this.Effects.Add(be);
            this.Effects.Add(sl);

            // We are currently testing...
            //this.Effects.Add(new RadialGradient());
        }

        EffectsCollection _effects = new EffectsCollection();

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public EffectsCollection Effects
        {
            get { return _effects; }
        }

        public class Settings
        {
            private Color startColor = Color.White;
            private Color enColorolor = Color.Black;

            [Editor(typeof(ColorTypeEditor), typeof(UITypeEditor))]
            public Color StartColor { get { return startColor; } set { startColor = value; } }
            public Color EnColorolor { get { return enColorolor; } set { enColorolor = value; } }
        }

        ProgressDialog progressDialog;
        private void UpdateImage()
        {
            progressDialog = new ProgressDialog();

            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += delegate
            {
                Graphics.FromImage(Background).Clear(Color.FromArgb(0, 0, 0, 0));
                foreach (Effect eff in this.Effects)
                {
                    eff.Apply(Background);
                }

                imagePreview.Image = Background;
            };

            bw.RunWorkerCompleted += delegate
            {
                progressDialog.Close();
            };

            bw.RunWorkerAsync();
            progressDialog.ShowDialog(this);
        }

        private void EffectPropertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            if (AutoUpdate.Checked)
                UpdateImage();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Type effType = Type.GetType("ImageEffect.Effects." + effectsCombo.Text);
            if (effType != null)
            {
                Effect effect = (Effect)(Activator.CreateInstance(effType));
                this.Effects.Add(effect);

                EffectPropertyGrid.SelectedObject = this.Effects;
            }

            if (AutoUpdate.Checked)
                UpdateImage();
        }

        private void deleteEffectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (EffectPropertyGrid.SelectedGridItem.Value.GetType().IsSubclassOf(typeof(Effect)))
            {
                Effect eff = (Effect)EffectPropertyGrid.SelectedGridItem.Value;
                Effects.Remove(eff);

                EffectPropertyGrid.SelectedObject = this.Effects;
                
                if (AutoUpdate.Checked)
                    UpdateImage();
            }
        }

        private void moveUpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (EffectPropertyGrid.SelectedGridItem.Value.GetType().IsSubclassOf(typeof(Effect)))
            {
                Effect eff = (Effect)EffectPropertyGrid.SelectedGridItem.Value;

                // get our indices to swap
                int idx1 = Effects.IndexOf(eff),
                    idx2 = Math.Max(0, idx1-1);

                // swap our values
                Effect tmp = this.Effects[idx1];
                this.Effects[idx1] = this.Effects[idx2];
                this.Effects[idx2] = tmp;

                // refresh our window
                EffectPropertyGrid.SelectedObject = this.Effects;
                
                if (AutoUpdate.Checked)
                    UpdateImage();
            }
        }

        private void moveDownToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (EffectPropertyGrid.SelectedGridItem.Value.GetType().IsSubclassOf(typeof(Effect)))
            {
                Effect eff = (Effect)EffectPropertyGrid.SelectedGridItem.Value;

                // get our indices to swap
                int idx1 = Effects.IndexOf(eff),
                    idx2 = Math.Min(Effects.Count-1, idx1 + 1);

                // swap our values
                Effect tmp = this.Effects[idx1];
                this.Effects[idx1] = this.Effects[idx2];
                this.Effects[idx2] = tmp;

                // refresh our window
                EffectPropertyGrid.SelectedObject = this.Effects;
                
                if (AutoUpdate.Checked)
                    UpdateImage();
            }
        }

        private void ImageResizeEvent(object sender, EventArgs e)
        {
            Background = new Bitmap(int.Parse(imageWidth.Text), int.Parse(imageHeight.Text));
            
            if (AutoUpdate.Checked)
                UpdateImage();
        }

        private void importButton_Click(object sender, EventArgs e)
        {
            importFileDialog.ShowDialog(this);
        }

        private void exportButton_Click(object sender, EventArgs e)
        {
            exportFileDialog.ShowDialog(this);
        }

        private void importFileDialog_FileOk(object sender, CancelEventArgs e)
        {
            if (!e.Cancel)
            {
                OpenFileDialog dlg = (OpenFileDialog)sender;

                Bitmap bmp = new Bitmap(Bitmap.FromFile(dlg.FileName));
                if (bmp != null)
                {
                    Effects.Insert(0, new ImageImport(bmp));

                    EffectPropertyGrid.SelectedObject = this.Effects;
                    UpdateImage();
                }
            }
        }

        private void exportFileDialog_FileOk(object sender, CancelEventArgs e)
        {
            if (!e.Cancel)
            {
                SaveFileDialog dlg = (SaveFileDialog )sender;

                UpdateImage();
                Background.Save(dlg.FileName);
            }
        }

        private void UpdateButton_Click(object sender, EventArgs e)
        {
            this.UpdateImage();
        }

        Effect _selectedEffect = null;
        private Effect GetEffect(object effect)
        {
            foreach (Effect eff in (EffectsCollection )this.EffectPropertyGrid.SelectedObject)
            {
                if (eff == effect)
                    return eff;
            }

            return null;
        }

        private void imagePreview_MouseDown(object sender, MouseEventArgs e)
        {
            // Get the nearest nested effect
            GridItem item = this.EffectPropertyGrid.SelectedGridItem;
            while (item != null && !item.Value.GetType().IsSubclassOf(typeof(Effect)))
            {
                item = item.Parent;
            }

            // Convert it to a read/write effect
            if (item != null)
            {
                _selectedEffect = GetEffect(item.Value);
            }
        }

        private void imagePreview_MouseUp(object sender, MouseEventArgs e)
        {
            _selectedEffect = null;
        }

        private void imagePreview_MouseLeave(object sender, EventArgs e)
        {
            _selectedEffect = null;
        }

        private Point _prevMousePos = new Point();
        private void imagePreview_MouseMove(object sender, MouseEventArgs e)
        {
            int dx = e.X - _prevMousePos.X;
            int dy = e.Y - _prevMousePos.Y;

            if (_selectedEffect != null)
            {
                _selectedEffect.Offset = new Point(
                    _selectedEffect.Offset.X + dx,
                    _selectedEffect.Offset.Y + dy);

                this.EffectPropertyGrid.Refresh();
            }
            
            _prevMousePos = new Point(e.X, e.Y);
        }
    }
}
